<?php


namespace App\Services;


use App\Models\Config;
use App\Models\Menu;
use App\Models\User;
use App\Models\UserExtra;
use App\Models\UserSecret;
use App\Utils\Code;
use App\Utils\Regex;
use App\Utils\Tools;
use App\Utils\Tree;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Tymon\JWTAuth\Contracts\JWTSubject;

class UserService extends BaseService
{

    /**
     * 用户登录
     * @param array $params
     * @return Builder|\Illuminate\Database\Eloquent\Model|object|JWTSubject|null
     * @throws \Exception
     */
    public function login(array $params) {
        $params['mode'] = isset($params['mode']) ? $params['mode'] : 'account';
        $rules = [
            'mode' => ['bail', 'required', 'in:account,sms,qr_code'],
            'phone' => ['bail', 'required', 'regex:'.Regex::regex['phone']],
            'secret' => ['bail', 'required']
        ];
        $names = [
            'phone' => '手机号码',
            'secret' => '登录密码'
        ];
        if ($params['mode'] == 'sms') {
            $names['secret'] = '短信验证码';
        } elseif ($params['mode'] == 'qr_code') {
            $names['secret'] = '身份认证标识';
        }
        $validator = \Validator::make($params, $rules, [], $names);
        if ($validator->fails()) {
            throw new \Exception($validator->errors()->first(), Code::PARAMETER_ERROR);
        }
        if (!$userInfo = User::where('phone', $params['phone'])->first()) {
            throw new \Exception('该用户不存在', Code::EMPTY_DATA);
        } elseif ($userInfo->status != 1) {
            throw new \Exception('该账户已停用', Code::LACK_OF_CONDITIONS);
        } elseif (isset($params['device']) && $params['device'] = 'pc' && $userInfo->is_admin != 1) {
            throw new \Exception('您没有登录权限', Code::LACK_OF_CONDITIONS);
        }
        $session = '';
        if (isset($params['mode']) && $params['mode'] == 'sms') {
            if (!Regex::check('captcha', $params['secret'])) {
                throw new \Exception('验证码是一个6位数字', Code::PARAMETER_ERROR);
            } elseif (!(new SmsService())->checkCaptcha($params['phone'], $params['secret'], true)) {
                throw new \Exception('短信验证码不正确', Code::PARAMETER_ERROR);
            }
        } elseif (isset($params['mode']) && $params['mode'] == 'qr_code') {
            if (!$this::$user) {
                throw new \Exception('你的终端设备还没有登录', Code::PARAMETER_ERROR);
            } elseif ($this::$user->phone != $params['phone']) {
                throw new \Exception('终端设备和登录账号不一致', Code::PARAMETER_ERROR);
            } elseif (!\Cache::pull("login_qr_code_{$params['secret']}", null)) {
                throw new \Exception('登录二维码已失效', Code::PARAMETER_ERROR);
            }
            $session = $params['secret'];
        } else {
            if (!Regex::check('password', $params['secret'])) {
                throw new \Exception('登录密码需是6-18位的字母数字组合', Code::PARAMETER_ERROR);
            }
            if (!$secret = $userInfo->secret()->where('mode', 'account')->value('secret')) {
                throw new \Exception('该用户没有设置登陆密码', Code::LACK_OF_CONDITIONS);
            } elseif (!\Hash::check($params['secret'], $secret)) {
                throw new \Exception('登陆密码不正确', Code::PARAMETER_ERROR);
            }
        }
        return $this->show($userInfo->user_id, true, true, true, $params['mode'], $session);
    }

    /**
     * 退出登陆
     * @param Request $request
     * @return bool
     * @throws \Exception
     */
    public function logout(Request $request){
        try {
            try {
                if (!$user_id = \Auth::id()){
                    throw new \Exception('你当前身份并没有登陆', Code::INCOMPLETE_DATA);
                } elseif (!\JWTAuth::parser()->setRequest($request)->hasToken() || !\JWTAuth::parseToken()->authenticate()) {
                    throw new \Exception('Token not provided', Code::INCOMPLETE_DATA);
                }
            } catch (\Exception $exception) {
                Code::log($exception->getMessage(), $exception->getCode());
                return true;
            }
            \Auth::guard()->logout();
            \JWTAuth::invalidate($token = \JWTAuth::getToken()->get());
            return (new UserSecret())->where('user_id', $user_id)
                    ->where('current_token', $token)->update(['current_token' => null]) == 1;
        } catch (\Exception $exception) {
            throw new \Exception($exception->getMessage(), Code::LACK_OF_CONDITIONS);
        }
    }

    /**
     * 创建数据
     * @param array $params
     * @param bool $isAdmin
     * @return mixed
     * @throws \Throwable
     */
    public function store(array $params, $isAdmin = false){
        if (!isset($params['secret']) || !$params['secret']) {
            $params['secret'] = substr(bcrypt(config('app.url')), 0, 18);
        }
        if (!$this->checkParams($params)){
            throw new \Exception('请求参数验证不通过'.$params['secret'], Code::PARAMETER_ERROR);
        }
        return DB::transaction(function () use ($params, $isAdmin){
            $model = new User();
            if (isset($params['phone']) && $params['phone']) {
                $model->phone = $params['phone'];
            }
            if ($this::$admin || $isAdmin){
                if (isset($params['is_admin'])){
                    $model->is_admin = (int)$params['is_admin'];
                }
                if (isset($params['group_id'])){
                    if ($params['group_id']) {
                        $model->group_id = ((new GroupService())->show($params['group_id']))->group_id;
                    } else {
                        $model->group_id = null;
                    }
                }
                if (isset($params['status'])){
                    $model->status = (int)$params['status'];
                }
            }
            if (isset($params['name'])){
                $model->name = $params['name'];
            }
            if (isset($params['avatar'])){
                $model->avatar = $params['avatar'];
            }
            $model->save();
            $extra = new UserExtra([
                'user_id' => $model->user_id,
                'bond' => isset($params['bond']) ? $params['bond'] : 0,
                'can_bid' => isset($params['can_bid']) ? $params['can_bid'] : 0,
                'created_at' => $model->created_at
            ]);
            $extra->save();
            $model->secret()->save(new UserSecret([
                'mode' => 'account',
                'secret' => bcrypt($params['secret'])
            ]));
            return $this->show($model->user_id, false);
        });
    }

    /**
     * 详细数据
     * @param null $id
     * @param bool $simple
     * @param bool $abnormal
     * @param false $isToken
     * @param string $mode
     * @param string $session
     * @param null $check
     * @param null $mark
     * @return Builder|\Illuminate\Database\Eloquent\Model|object|JWTSubject|null
     * @throws \Exception
     */
    public function show($id = null, $simple = true, $abnormal = true, $isToken = false, $mode = 'account', $session = '', $check = null, $mark = null){
        $model = User::where('user_id', $id ? (int)$id : \Auth::id());
        $with = ['extra'];
        if (!$simple && $source = request()->get('source')){
            if ($source == 'manager') {
                $with['group'] = function(HasOne $hasOne) {
                    $hasOne->select(['group_id', 'group_name'])
                        ->with(['menus' => function(BelongsToMany $belongsToMany) {
                            $belongsToMany->where('module', 1)->where('status', 1);
                    }]);
                };
            }
        }
        $model = $model->with($with)->first();
        if (!$model && $abnormal) {
            throw new \Exception('不存在的用户ID主键', Code::EMPTY_DATA);
        }
        if ($check) {
            if ($check != Config::where('type', 1)
                    ->where('key', 'check_in_pwd')->value('value')) {
                throw new \Exception('激活码不正确', Code::PARAMETER_ERROR);
            }
            $model->check_in_time = date('Y-m-d H:i:s');
            $model->save();
        }
        // 附加用户身份密钥
        if ($isToken) {
            /** @var JWTSubject $model */
            if (!$token = \JWTAuth::fromUser($model)) {
                throw new \Exception('用户登陆遇到错误', Code::INVALID_DATA);
            };
            if (!$secret = UserSecret::where('user_id', $model->user_id)->where('mode', $mode)->first()) {
                $secret = new UserSecret();
                $secret->user_id = $model->user_id;
                $secret->mode = $mode;
                $secret->secret = substr(bcrypt(config('app.url')), 0, 18);
            };
            $secret->session = $session;
            $model->setAttribute('authorization', 'Bearer ' . $secret->current_token = $token);
            if ($mode == 'wx_xcx') {
                $model->setAttribute('cookie', $secret->cookie = Tools::instance()->md5($model->user_id.time()));
            } else {
                $cookie = Tools::instance()->md5($model->user_id.time());
                DB::table('user_secrets')->where('user_id', $model->user_id)
                    ->whereIn('mode', ['qr_code','sms','account'])->update(['cookie' => $cookie]);
                $model->setAttribute('cookie', $secret->cookie = $cookie);
            }
            $secret->save();
        }
        return $model;
    }

    /**
     * 更新数据
     * @param array $params
     * @param null $id
     * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model|object|null
     * @throws \Exception
     */
    public function update(array $params, $id){
        if (!$this->checkParams($params, $id, true)){
            throw new \Exception('请求参数验证不通过', Code::PARAMETER_ERROR);
        }
        $model = $this->show($id, true, true);
        if (isset($params['phone']) && $params['phone'] && $model->phone != $params['phone']) {
            if (User::where('phone', $params['phone'])->exists()) {
                throw new \Exception('该手机号码被使用', Code::PARAMETER_ERROR);
            }
            $model->phone = $params['phone'];
        }
        if (isset($params['number']) && $params['number'] && $model->number != $params['number']) {
            if (User::where('number', $params['number'])->exists()) {
                throw new \Exception('该会员号已被使用', Code::PARAMETER_ERROR);
            }
            $model->number = $params['number'];
        }
        if ($this::$admin){
            if (array_key_exists('is_admin', $params)){
                $model->is_admin = (int)$params['is_admin'];
            }
            if (array_key_exists('group_id', $params)){
                $model->group_id = $params['group_id'] ? ((new GroupService())->show($params['group_id']))->group_id : null;
            }
            if (array_key_exists('status', $params)){
                $model->status = (int)$params['status'];
            }
        }
        if (isset($params['name']) && $params['name']){
            $model->name = $params['name'];
        }
        if (isset($params['avatar']) && $params['avatar']){
            $model->avatar = $params['avatar'];
        }
        if (isset($params['remarks']) && $params['remarks']){
            $model->remarks = $params['remarks'];
        }
        if (isset($params['bond']) && is_numeric($params['bond'])) {
            $model->extra->bond = (int)$params['bond'];
        }
        if (isset($params['can_bid']) && in_array((int)$params['can_bid'], [0, 1])) {
            $model->extra->can_bid = (int)$params['can_bid'];
        }
        if (isset($params['secret']) && ($this::$admin || \Auth::id() == $model->user_id)){
            if (!$secretInfo = UserSecret::where('user_id', $model->user_id)
                ->where('mode', 'account')->first()){
                $secretInfo = new UserSecret();
                $secretInfo->user_id = $model->user_id;
                $secretInfo->mode = 'account';
            }
            $secretInfo->secret = bcrypt($params['secret']);
            $secretInfo->save();
        }
        $model->save();
        $model->extra->save();
        return $this->show($model->user_id, false);
    }

    public function updateAll(array $params = []) {
        $model = new UserExtra();
        if (isset($params['user_ids']) && is_array($params['user_ids'])) {
            $model = $model->whereIn('user_id', $params['user_ids']);
        } else {
            $model = $model->leftJoin('users', 'user_extra.user_id', '=', 'users.user_id');
            if (isset($params['created_at']) && $params['created_at'] && is_array($params['created_at'])) {
                if (isset($params['created_at'][0]) && $params['created_at'][0]) {
                    $model = $model->where('users.created_at', '>=', $params['created_at'][0]);
                }
                if (isset($params['created_at'][1]) && $params['created_at'][1]) {
                    $model = $model->where('users.created_at', '<=', $params['created_at'][1]);
                }
            }
            if (isset($params['phone']) && $params['phone']){
                $model = $model->where('users.phone', $params['phone']);
            }
            if (isset($params['name']) && $params['name']){
                $model = $model->where('users.name', 'like', '%'.$params['name'].'%');
            }
            if (isset($params['remarks']) && $params['remarks']){
                $model = $model->where('users.remarks', 'like', '%'.$params['remarks'].'%');
            }
            if (isset($params['group_id']) && is_numeric($params['group_id'])){
                $model = $model->where('users.group_id', (int)$params['group_id']);
            }
            if (isset($params['status']) && is_numeric($params['status'])){
                $model = $model->where('users.status', (int)$params['status']);
            }
        }
        $data = [];
        if (isset($params['can_bid']) && in_array((int)$params['can_bid'], [1, 0])) {
            $data['can_bid'] = (int)$params['can_bid'];
        }
        if (isset($params['bond']) && (int)$params['bond'] == 0) {
            $data['bond'] = 0;
        }
        return $model->update($data);
    }

    /**
     * 用户列表
     * @param array $params
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
     */
    public function index(array $params = []){
        $model = User::with([
            'group'=>function(HasOne $hasOne) {
                $hasOne->select(['group_id', 'group_name']);
            },
            'extra'
        ]);
        if (isset($params['created_at']) && $params['created_at'] && is_array($params['created_at'])) {
            if (isset($params['created_at'][0]) && $params['created_at'][0]) {
                $model = $model->where('created_at', '>=', $params['created_at'][0]);
            }
            if (isset($params['created_at'][1]) && $params['created_at'][1]) {
                $model = $model->where('created_at', '<=', $params['created_at'][1]);
            }
        }
        if (isset($params['phone']) && $params['phone']){
            $model = $model->where('phone', $params['phone']);
        }
        if (isset($params['number']) && $params['number']){
            $model = $model->where('number', $params['number']);
        }
        if (isset($params['name']) && $params['name']){
            $model = $model->where('name', 'like', '%'.$params['name'].'%');
        }
        if (isset($params['remarks']) && $params['remarks']){
            $model = $model->where('remarks', 'like', '%'.$params['remarks'].'%');
        }
        if (isset($params['group_id']) && is_numeric($params['group_id'])){
            if ($params['group_id'] == 0) {
                $model = $model->whereNull('group_id');
            } else {
                $model = $model->where('group_id', (int)$params['group_id']);
            }
        }
        if (isset($params['status']) && is_numeric($params['status'])){
            $model = $model->where('status', (int)$params['status']);
        }
        if (isset($params['is_exoprt']) && $params['is_exoprt'] == 1) {
            return $model->orderBy('created_at', 'desc')->get();
        } else {
            return $model->orderBy('created_at', 'desc')
                ->paginate(isset($params['per_page']) ? (int)$params['per_page'] : 15);
        }
    }

    /**
     * 删除数据
     * @param $id
     * @return bool|mixed|null
     * @throws \Exception
     */
    public function destroy($id) {
        $info = $this->show($id, true);
        if (!$this::$admin){
            throw new \Exception('你没有删除用户的权限', Code::LACK_OF_CONDITIONS);
        }
        return $info->delete();
    }

    /**
     * 用户菜单
     * @param array $params
     * @return array
     * @throws \Exception
     */
    public function menus(array $params = []) {
        $userInfo = $this->show($this::$user->user_id, true, true);
        $model = Menu::where('module', 1);
        if ($userInfo->user_id != config('app.admin_id')) {
            $model = $model->whereHas('groups', function (Builder $builder) {
                $builder->where('bind_group_menu.group_id', $this::$user->group_id);
            });
        }
        $lists = $model->select(['menu_id','menu_name','pid','uri','is_hide'])->orderBy('sort', 'asc')->get();
        if (isset($params['format']) && $params['format'] == 'list'){
            $lists = Tree::instance()->treeFormat($lists->toArray(), 'children', null);
        } else {
            $lists = Tree::instance()->toTree($lists->toArray(), 'menu_id');
        }
        return $lists;
    }

    /**
     * 验证权限
     * @param $source
     * @param $cookie
     * @return bool
     * @throws \Exception
     */
    public function checkAsk($source, $cookie) {
        if (in_array($source, ['api','manager'])) {
            if (!\config('app.debug')) {
                if (!$cookie) {
                    throw new \Exception('请求缺少终端身份标识', Code::EXPIRED);
                }
                if (UserSecret::where('user_id', \Auth::id())->where('cookie', $cookie)->doesntExist()) {
                    throw new \Exception('该用户已在其他终端登录', Code::EXPIRED);
                }
            }
            // 获取用户信息
            if (!$userInfo = $this->show(\Auth::id(), true, false)) {
                throw new \Exception('你还没有登陆', Code::EXPIRED);
            } elseif ($userInfo->status != 1) {
                throw new \Exception('该账户已停用', Code::EXPIRED);
            }

            // 获取请求路径
            $router = '/' . \Route::current()->uri;
            // 获取菜单列表
            $menuList = Menu::where('module', (int)($source == 'manager'))
                ->where(function (Builder $builder) use ($router){
                    $builder->where('uri', $router)->orWhereHas('rules', function (Builder $builder) use ($router) {
                        $builder->where('uri', $router)->where('status', 1);
                    });
                })->where('status', 1)->get();
            $router = str_replace(['/manager', '/api', '?'], '', $router);
            // 后台非超管验证权限
            if ($userInfo->user_id != config('app.admin_id') && $source == 'manager') {
                if (!count($menuList)) {
//                    throw new \Exception('你请求的接口不存在', Code::EMPTY_DATA);
                } elseif (!$userInfo->group) {
                    throw new \Exception('你没有登录权限', Code::EXPIRED);
                } elseif ($userInfo->group->status != 1) {
                    throw new \Exception('你的授权身份已经停用', Code::EXPIRED);
                } elseif (DB::table('bind_group_menu')->where('group_id', $userInfo->group_id)->doesntExist()) {
                    throw new \Exception('你还没有进行登录授权', Code::EXPIRED);
                } elseif (DB::table('bind_group_menu')->where('group_id', $userInfo->group_id)
                    ->whereIn('menu_id', $menuList->pluck('menu_id')->toArray())->doesntExist()) {
                    // 排除获取权限的用户信息接口
                    $user_id = \request()->route('id');
                    if ($router == '/user/info/{id}' && $user_id && $user_id != \Auth::id()) {
//                        throw new \Exception('你没有权限使用该功能', Code::LACK_OF_CONDITIONS);
                    }
                }
            } elseif (count($menuList) && $source == 'api') {

            }
            if ($router !== '/user/update/{id}') {

            }
        }
        return true;
    }

    /**
     * 验证参数
     * @param array $params
     * @param null $id
     * @param bool $isUpdate
     * @return array
     * @throws \Exception
     */
    private function checkParams(array $params, $id = null, $isUpdate = false){
        $rules = [
            'is_admin' => 'bail|in:0,1',
            'phone' => ['bail', 'nullable', 'regex:'.Regex::regex['phone']],
            'secret' => ['bail', 'nullable', 'regex:' . Regex::regex['password']],
            'group_id' => ['bail', 'nullable', 'integer'],
            'captcha' => ['bail', 'integer', 'digits:6'],
            'status' => 'bail|in:0,1'
        ];
        if ($isUpdate){
            // 删除必选条件
            foreach ($rules as $field => &$rule){
                if (isset($rule[1]) && $rule[1] == 'required') {
                    $rule[1] = 'nullable';
                }
            }
        }
        $validator = Validator::make($params, $rules, [
            'secret.regex' => '有效的密码为6-18位之间的字母数字组合',
            'phone.regex' => '请输入有效的手机号码',
        ], [
            'is_admin' => '用户身份',
            'phone' => '手机号码',
            'secret' => '登陆密码',
            'group_id' => '权限分组',
            'captcha' => '验证码',
            'status' => '用户状态'
        ]);
        if ($validator->fails()) {
            throw new \Exception($validator->errors()->first(), Code::PARAMETER_ERROR);
        }
        return $params;
    }
}
